//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================
//-----------------------------------------------------------------------------
// Net IP.C
// This module is the IP layer
// Refer to RFC 791, 1122, and RFC 815 (fragmentation)
//-----------------------------------------------------------------------------
#include "lh7a400_map.h"
#include "LH7A400_drivers.h"
#include "watertek_drivers.h"
#include "net_net.h"
#include "watertek_main.h"

#define UDP_DATA_FRAME_OFFSET  (sizeof(ETH_HEADER) + sizeof(IP_HEADER) + sizeof(UDP_HEADER))

WAIT  wait1;

//lcd
extern UNS_16 lcdCurX;
extern UNS_16 lcdCurY;

extern UNS_8  my_hwaddr[];
extern UNS_8  broadcast_hwaddr[];
extern UNS_32 my_ipaddr;

extern UNS_8 prt;

//------------------------------------------------------------------------
// This routine will calculation the complemented 16-bit 1's complement sum
// required to compute network CRCs.  The CRC can be calculated over two
// different memory regions.  If only one region is desired, then the other's
// length can be set to 0.  Also, if an odd number of UNS_8s are specified,
// the routine will add a 0 to the end of the data before doing the CRC and will
//  include that 0 in the calculation.
//------------------------------------------------------------------------
UNS_16 CRC(UNS_16 *pwRegion1, UNS_16 wLength1, UNS_16 *pwRegion2, UNS_16 wLength2)
{

    UNS_32 dwSum, dwCarryOut;
    UNS_16 wCRC;
    UNS_16 i;

    // There is no need to swap for network ordering during calculation because of the end around
        //  carry used in 1's complement addition
    dwSum = 0;
    if (wLength1 & 1) {
        *((UNS_8 *)pwRegion1 + wLength1) = 0;
        wLength1++;
    }
    wLength1 >>= 1;
    for (i = 0; i < wLength1; i++)
        dwSum += *pwRegion1++;

    if (wLength2 & 1) {
        *((UNS_8 *)pwRegion2 + wLength2) = 0;
        wLength2++;
    }
    wLength2 >>= 1;
    for (i = 0; i < wLength2; i++)
        dwSum += *pwRegion2++;

    // Now, we have to add back in all the carry out's from the lower 16 bits
        //  because this is 1's complement
    while (dwSum & 0xFFFF0000UL) {
        dwCarryOut = dwSum >> 16;
        dwSum &= 0x0000FFFFUL;
        dwSum += dwCarryOut;
    }

    wCRC = (UNS_16)dwSum;

    // There is no need to flip for network UNS_8 order because we did all the sums backwards
        //  already.
    wCRC = ~wCRC;

    return wCRC;
}

//------------------------------------------------------------------------
//  Format UDP headers into supplied frame buffer,
//  and send to peer.
//------------------------------------------------------------------------
void send_udp(
    UNS_8 *framebuf,
    UNS_32 dest_ipaddr,
    UNS_16 dest_port,
    UNS_32 source_ipaddr,
    UNS_16 source_port,
    UNS_8 *udpdata,
    UNS_16 length)
{
    UDP_PSEUDO_HEADER udp_pseudo_hdr;
    UDP_HEADER *udp = NULL;

    // The field information comes from page 145 of TCP/IP Illustrated by W. Richard Stevens
    // Construct the UDP pseudo header
    udp_pseudo_hdr.source_ipaddr = source_ipaddr;
    udp_pseudo_hdr.dest_ipaddr = dest_ipaddr;
    udp_pseudo_hdr.zero = 0;
    udp_pseudo_hdr.protocol_id = UDP_TYPE;  // This is the code for the UDP protocol
    udp_pseudo_hdr.total_length = //htons(sizeof(UDP_HEADER) + length);
                                    INTSWAP(sizeof(UDP_HEADER) + length);

    // Construct the real UDP header
    udp = (UDP_HEADER *)(UNS_8 *)(framebuf + sizeof(IP_HEADER) + sizeof(ETH_HEADER));
    udp->source_port = source_port;
    udp->dest_port = dest_port;

    // The total length of the UDP datagram
    udp->udp_length = udp_pseudo_hdr.total_length;
    udp->udp_checksum = 0;     // Set the CRC to 0 while it is being computed

    // Copy user data if necessary
    if ((udpdata - framebuf) != UDP_DATA_FRAME_OFFSET)
        memcpy((void *)((UNS_8 *)udp + sizeof(UDP_HEADER)), udpdata, length);

    // Compute the CRC for the UDP datagram.
    // The pad 0 will automatically be added if there are an odd number of UNS_8s
    udp->udp_checksum = CRC((UNS_16 *)(&udp_pseudo_hdr), sizeof(UDP_PSEUDO_HEADER),
                            (UNS_16 *)udp, INTSWAP(udp->udp_length));
    if (udp->udp_checksum == 0)
        udp->udp_checksum = ~(udp->udp_checksum);

    DBGMSG("       UDP  : send a udp !    " );
    //dump(framebuf+sizeof(ETH_HEADER), 0x100);
    ip_send(framebuf, dest_ipaddr, UDP_TYPE, sizeof(UDP_HEADER) + length);
    DBGMSG("       UDP  : send a udp OK!    " );
}

//------------------------------------------------------------------------
// This routine will check a UDP frame that has been received.  It will make
// sure that it was for our IP address and that the checksums are right and
// that it's a UDP packet.  If something is wrong, the packet will be discarded
// and the routine will return non-zero.  If everything is right, then all the
// port and IP information will be filled out and the routine will return 0.
// Note that if we are doing the DHCP process, the DHCP server will send the
// OFFER packet to the IP address that it would like to give us.  We have to
// have be able to accept a packet for any IP address in that case.
// This condition is signaled to the routine by fPromiscuousIP == 1, which is
// set using SetPromiscuousIP()
//------------------------------------------------------------------------
UNS_16 check_udp(UNS_8 *ipbuf, UNS_16 *pdest_port,
    UNS_16 *psource_port, UNS_16 **ppdata, UNS_16 *plength)
{
    IP_HEADER *ip;
    UDP_PSEUDO_HEADER udp_pseudo_hdr;
    UDP_HEADER *udp;

    // Note that I don't do any checking that depends on a length field in the packet until after the CRC
        //  is verified for that data.  This prevents the code from running past the end of buffers, etc. when
        //  a bad packet is received.

    ip = (IP_HEADER *)(ipbuf);

    // Build the UDP Pseudo Header
    udp_pseudo_hdr.source_ipaddr = ip->source_ipaddr;
    udp_pseudo_hdr.dest_ipaddr = ip->dest_ipaddr;
    udp_pseudo_hdr.zero = 0;
    udp_pseudo_hdr.protocol_id = UDP_TYPE;   // This is the code for the UDP protocol
    udp_pseudo_hdr.total_length = INTSWAP(INTSWAP(ip->total_length) - sizeof(IP_HEADER));

    // Check the UDP checksum, I'm using the cwTotalUDPLength calculated from the IP header info because
        //  we know that it's not corrupted and won't give an outrageous length for the packet
    udp = (UDP_HEADER *)((UNS_8 *)ip + sizeof(IP_HEADER));

    if (CRC((UNS_16 *)(&udp_pseudo_hdr), sizeof(UDP_PSEUDO_HEADER),
        (UNS_16 *)udp, INTSWAP(udp_pseudo_hdr.total_length)) != 0) {
        DBGMSG("UDP: Header checksum failure! " );
        return 4;
    }

    // Now we know we have a good packet, fill out the fields
    *pdest_port = udp->dest_port;
    *psource_port = udp->source_port;
    *ppdata = (UNS_16 *)((UNS_8 *)udp + sizeof(UDP_HEADER));
    *plength = INTSWAP(udp->udp_length) - sizeof(UDP_HEADER);

    return 0;
}
